package io.kimmking.rpcfx.client;

import com.alibaba.fastjson.JSON;
import io.kimmking.rpcfx.api.RpcfxRequest;
import io.kimmking.rpcfx.demo.api.RpcfxException;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.logging.Logger;

/**
 * 2.（选做）使用 netty 实现后端 http 访问
 */
@Slf4j
public class NettyHttpClient {

    private static final Logger logger = Logger.getLogger(NettyHttpClient.class.getName());

    private int port;

    private String host;

    private RpcfxRequest req;

    public NettyHttpClient(int port, String host, RpcfxRequest req) {
        this.port = port;
        this.host = host;
        this.req = req;
    }

    public String connect() throws Exception{
        // 配置客户端NIO线程组
        EventLoopGroup group = new NioEventLoopGroup();
        HttpClientHandler httpClientHandler = new HttpClientHandler(req);
        try {
            Bootstrap b = new Bootstrap();
            b.group(group).channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    .handler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline p = socketChannel.pipeline();
                            p.addLast(new HttpClientCodec());
                            p.addLast(new HttpObjectAggregator(1024 * 1024));
                            //解压
                            p.addLast(new HttpContentDecompressor());
                            p.addLast(httpClientHandler);
                        }
                    });
            // 发起异步连接操作
            ChannelFuture f = b.connect(host, port).sync();
            while (httpClientHandler.getResult() == null) {
                Thread.sleep(100);
            }
            f.channel().close().sync();
            return httpClientHandler.getResult();
        } finally {
            // 优雅退出，释放NIO线程组
            group.shutdownGracefully();
        }
    }

    static class HttpClientHandler extends ChannelInboundHandlerAdapter {

        private RpcfxRequest req;

        HttpClientHandler(RpcfxRequest req) {
            this.req = req;
        }

        private String result;

        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) {
            ctx.flush();
        }

        @Override
        public void channelActive(ChannelHandlerContext ctx) throws URISyntaxException, UnsupportedEncodingException {
            URI url = new URI("/");
            //配置HttpRequest的请求数据和一些配置信息
            FullHttpRequest request = new DefaultFullHttpRequest(
                    HttpVersion.HTTP_1_0, HttpMethod.POST, url.toASCIIString(), Unpooled.wrappedBuffer(JSON.toJSONString(req).getBytes(StandardCharsets.UTF_8)));

            request.headers()
                    .set(HttpHeaderNames.CONTENT_TYPE, "application/json; charset=utf-8")
                    //开启长连接
                    .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE)
                    //设置传递请求内容的长度
                    .set(HttpHeaderNames.CONTENT_LENGTH, request.content().readableBytes());

            //发送数据
            ctx.writeAndFlush(request);
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            FullHttpResponse response = (FullHttpResponse) msg;

            ByteBuf content = response.content();

            result = content.toString(CharsetUtil.UTF_8);
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            // 释放资源
            log.warn("Unexpected exception from downstream : " + cause.getMessage());
            ctx.close();
        }

        public String getResult() {
            return result;
        }

        public void setResult(String result) {
            this.result = result;
        }

    }

    public String post() throws RpcfxException {
        try {
            return connect();
        } catch (Exception e) {
            log.error("服务请求异常", e);
            throw new RpcfxException("服务请求异常");
        }
    }

    public static void main(String[] args) throws Exception {
        RpcfxRequest request = new RpcfxRequest();
        request.setServiceClass("io.kimmking.rpcfx.demo.api.UserService");
        request.setMethod("findById");
        request.setParams(new Object[]{1});
        int port = 8080;
        Object result = new NettyHttpClient(port, "localhost", request).post();
        System.out.println("请求结果：" + result);
    }

}

